﻿using Fall.Core.Extensions;
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace Fall.Core.Reflaction
{
    public class ObjectHelper
    {
        private static readonly ConcurrentDictionary<string, PropertyInfo> CachedObjectProperties =
            new ConcurrentDictionary<string, PropertyInfo>();

        public static void TrySetProperty<TObject, TValue>(
            TObject obj,
            Expression<Func<TObject, TValue>> propertySelector,
            Func<TValue> valueFactory)
        {
            TrySetProperty(obj, propertySelector, x => valueFactory());
        }

        public static void TrySetProperty<TObject, TValue>(
            TObject obj,
            Expression<Func<TObject, TValue>> propertySelector,
            Func<TObject, TValue> valueFactory)
        {
            var property = GetProperty(obj, propertySelector);
            property?.SetValue(obj, valueFactory(obj));
        }

        private static PropertyInfo GetProperty<TObject, TValue>(TObject obj,
            Expression<Func<TObject, TValue>> propertySelector)
        {
            if (propertySelector.Body.NodeType != ExpressionType.MemberAccess)
            {
                return null;
            }

            var memberExpression = propertySelector.Body.As<MemberExpression>();

            var propertyInfo = obj.GetType().GetProperties().FirstOrDefault(x =>
                x.Name == memberExpression.Member.Name &&
                x.GetSetMethod(true) != null);

            if (propertyInfo == null)
            {
                return null;
            }
            return propertyInfo;
        }
    }
}
